//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// C# Port port by: Lars Brubaker
//                  larsbrubaker@gmail.com
// Copyright (C) 2007
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// class ClippingPixelFormtProxy
//
//----------------------------------------------------------------------------
using System;
using System.IO;
using AGG;

namespace AGG.Image
{
    public class ImageClippingProxy : ImageProxy
    {
        private rect_i m_ClippingRect;

        public const byte cover_full = 255;

        public ImageClippingProxy(IImageByte ren)
            : base(ren)
        {
            m_ClippingRect = new rect_i(0, 0, (int)ren.Width - 1, (int)ren.Height - 1);
        }

        public override void LinkToImage(IImageByte ren)
        {
            base.LinkToImage(ren);
            m_ClippingRect = new rect_i(0, 0, (int)ren.Width - 1, (int)ren.Height - 1);
        }

        public bool SetClippingBox(int x1, int y1, int x2, int y2)
        {
            rect_i cb = new rect_i(x1, y1, x2, y2);
            cb.normalize();
            if (cb.clip(new rect_i(0, 0, (int)Width - 1, (int)Height - 1)))
            {
                m_ClippingRect = cb;
                return true;
            }
            m_ClippingRect.x1 = 1;
            m_ClippingRect.y1 = 1;
            m_ClippingRect.x2 = 0;
            m_ClippingRect.y2 = 0;
            return false;
        }

        public void reset_clipping(bool visibility)
        {
            if(visibility)
            {
                m_ClippingRect.x1 = 0;
                m_ClippingRect.y1 = 0;
                m_ClippingRect.x2 = (int)Width - 1;
                m_ClippingRect.y2 = (int)Height - 1;
            }
            else
            {
                m_ClippingRect.x1 = 1;
                m_ClippingRect.y1 = 1;
                m_ClippingRect.x2 = 0;
                m_ClippingRect.y2 = 0;
            }
        }

        public void clip_box_naked(int x1, int y1, int x2, int y2)
        {
            m_ClippingRect.x1 = x1;
            m_ClippingRect.y1 = y1;
            m_ClippingRect.x2 = x2;
            m_ClippingRect.y2 = y2;
        }

        public bool inbox(int x, int y)
        {
            return x >= m_ClippingRect.x1 && y >= m_ClippingRect.y1 &&
                   x <= m_ClippingRect.x2 && y <= m_ClippingRect.y2;
        }

        public rect_i clip_box() { return m_ClippingRect;    }
        int xmin() { return m_ClippingRect.x1; }
        int ymin() { return m_ClippingRect.y1; }
        int xmax() { return m_ClippingRect.x2; }
        int ymax() { return m_ClippingRect.y2; }

        public rect_i bounding_clip_box() { return m_ClippingRect;    }
        public int bounding_xmin() { return m_ClippingRect.x1; }
        public int bounding_ymin() { return m_ClippingRect.y1; }
        public int bounding_xmax() { return m_ClippingRect.x2; }
        public int bounding_ymax() { return m_ClippingRect.y2; }

        public void clear(IColorType in_c)
        {
            int y;
            RGBA_Bytes c = new RGBA_Bytes(in_c.R_Byte, in_c.G_Byte, in_c.B_Byte, in_c.A_Byte);
            if(Width != 0)
            {
                for(y = 0; y < Height; y++)
                {
                    base.copy_hline(0, (int)y, (int)Width, c);
                }
            }
        }
          
        public override void copy_pixel(int x, int y, byte[] c, int ByteOffset)
        {
            if(inbox(x, y))
            {
                base.copy_pixel(x, y, c, ByteOffset);
            }
        }

        public override RGBA_Bytes pixel(int x, int y)
        {
            return inbox(x, y) ? base.pixel(x, y) : new RGBA_Bytes();
        }

        public override void copy_hline(int x1, int y, int x2, RGBA_Bytes c)
        {
            if(x1 > x2) { int t = (int)x2; x2 = (int)x1; x1 = t; }
            if(y  > ymax()) return;
            if(y  < ymin()) return;
            if(x1 > xmax()) return;
            if(x2 < xmin()) return;

            if(x1 < xmin()) x1 = xmin();
            if(x2 > xmax()) x2 = (int)xmax();

            base.copy_hline(x1, y, (int)(x2 - x1 + 1), c);
        }

        public override void copy_vline(int x, int y1, int y2, RGBA_Bytes c)
        {
            if(y1 > y2) { int t = (int)y2; y2 = (int)y1; y1 = t; }
            if(x  > xmax()) return;
            if(x  < xmin()) return;
            if(y1 > ymax()) return;
            if(y2 < ymin()) return;

            if(y1 < ymin()) y1 = ymin();
            if(y2 > ymax()) y2 = (int)ymax();

            base.copy_vline(x, y1, (int)(y2 - y1 + 1), c);
        }

        public override void blend_hline(int x1, int y, int x2, RGBA_Bytes c, byte cover)
        {
            if (x1 > x2) 
            {
                int t = (int)x2;
                x2 = x1;
                x1 = t; 
            }
            if(y  > ymax()) 
                return;
            if(y  < ymin()) 
                return;
            if(x1 > xmax()) 
                return;
            if(x2 < xmin()) 
                return;

            if(x1 < xmin()) 
                x1 = xmin();
            if(x2 > xmax()) 
                x2 = xmax();

            base.blend_hline(x1, y, x2, c, cover);
        }

        public override void blend_vline(int x, int y1, int y2, RGBA_Bytes c, byte cover)
        {
            if(y1 > y2) { int t = y2; y2 = y1; y1 = t; }
            if(x  > xmax()) return;
            if(x  < xmin()) return;
            if(y1 > ymax()) return;
            if(y2 < ymin()) return;

            if(y1 < ymin()) y1 = ymin();
            if(y2 > ymax()) y2 = ymax();

            base.blend_vline(x, y1, y2, c, cover);
        }

        public override void blend_solid_hspan(int x, int y, int len, RGBA_Bytes c, byte[] covers, int coversIndex)
        {
#if false
            FileStream file = new FileStream("pixels.txt", FileMode.Append, FileAccess.Write);
            StreamWriter sw = new StreamWriter(file);
            sw.Write("h-x=" + x.ToString() + ",y=" + y.ToString() + ",len=" + len.ToString() + "\n");
            sw.Close();
            file.Close();
#endif

            if (y > ymax()) return;
            if (y < ymin()) return;

            if (x < xmin())
            {
                len -= xmin() - x;
                if (len <= 0) return;
                coversIndex += xmin() - x;
                x = xmin();
            }
            if (x + len > xmax())
            {
                len = xmax() - x + 1;
                if (len <= 0) return;
            }
            base.blend_solid_hspan(x, y, len, c, covers, coversIndex);
        }

        public override void blend_solid_vspan(int x, int y, int len, RGBA_Bytes c, byte[] covers, int coversIndex)
        {
#if false
            FileStream file = new FileStream("pixels.txt", FileMode.Append, FileAccess.Write);
            StreamWriter sw = new StreamWriter(file);
            sw.Write("v-x=" + x.ToString() + ",y=" + y.ToString() + ",len=" + len.ToString() + "\n");
            sw.Close();
            file.Close();
#endif

            if (x > xmax()) return;
            if (x < xmin()) return;
             
            if (y < ymin())
            {
                len -= (ymin() - y);
                if (len <= 0) return;
                coversIndex += ymin() - y;
                y = ymin();
            }
            if (y + len > ymax())
            {
                len = (ymax() - y + 1);
                if (len <= 0) return;
            }
            base.blend_solid_vspan(x, y, len, c, covers, coversIndex);
        }

        public override void copy_color_hspan(int x, int y, int len, RGBA_Bytes[] colors, int colorsIndex)
        {
            if (y > ymax()) return;
            if (y < ymin()) return;

            if (x < xmin())
            {
                int d = xmin() - x;
                len -= d;
                if (len <= 0) return;
                colorsIndex += d;
                x = xmin();
            }
            if (x + len > xmax())
            {
                len = (xmax() - x + 1);
                if (len <= 0) return;
            }
            base.copy_color_hspan(x, y, len, colors, colorsIndex);
        }

        public override void copy_color_vspan(int x, int y, int len, RGBA_Bytes[] colors, int colorsIndex)
        {
            if (x > xmax()) return;
            if (x < xmin()) return;

            if (y < ymin())
            {
                int d = ymin() - y;
                len -= d;
                if (len <= 0) return;
                colorsIndex += d;
                y = ymin();
            }
            if (y + len > ymax())
            {
                len = (ymax() - y + 1);
                if (len <= 0) return;
            }
            base.copy_color_vspan(x, y, len, colors, colorsIndex);
        }

        public override void blend_color_hspan(int x, int y, int in_len, RGBA_Bytes[] colors, int colorsIndex, byte[] covers, int coversIndex, bool firstCoverForAll)
        {
            int len = (int)in_len;
            if (y > ymax())
                return;
            if (y < ymin())
                return;

            if (x < xmin())
            {
                int d = xmin() - x;
                len -= d;
                if (len <= 0) return;
                if (covers != null) coversIndex += d;
                colorsIndex += d;
                x = xmin();
            }
            if (x + len - 1 > xmax())
            {
                len = xmax() - x + 1;
                if (len <= 0) return;
            }

            base.blend_color_hspan(x, y, len, colors, colorsIndex, covers, coversIndex, firstCoverForAll);
        }

        public void copy_from(IImageByte src)
        {
            CopyFrom(src, new rect_i(0, 0, (int)src.Width, (int)src.Height), 0, 0);
        }

        public override void SetPixelFromColor(byte[] p, IColorType c)
        {
            throw new System.NotImplementedException();
        }

        public override void CopyFrom(IImageByte sourceImage,
                       rect_i sourceImageRect,
                       int destXOffset,
                       int destYOffset)
        {
            rect_i destRect = sourceImageRect;
            destRect.Offset(destXOffset, destYOffset);

            rect_i clippedSourceRect = new rect_i();
            if(clippedSourceRect.intersect_rectangles(destRect, m_ClippingRect))
            {
                // move it back relative to the source
                clippedSourceRect.Offset(-destXOffset, -destYOffset);

                base.CopyFrom(sourceImage, clippedSourceRect, destXOffset, destYOffset);
            }
        }

        public rect_i clip_rect_area(ref rect_i destRect, ref rect_i sourceRect, int sourceWidth, int sourceHeight)
        {
            rect_i rc = new rect_i(0,0,0,0);
            rect_i cb = clip_box();
            ++cb.x2;
            ++cb.y2;

            if(sourceRect.x1 < 0)
            {
                destRect.x1 -= sourceRect.x1;
                sourceRect.x1 = 0;
            }
            if(sourceRect.y1 < 0)
            {
                destRect.y1 -= sourceRect.y1;
                sourceRect.y1 = 0;
            }

            if(sourceRect.x2 > sourceWidth) sourceRect.x2 = sourceWidth;
            if(sourceRect.y2 > sourceHeight) sourceRect.y2 = sourceHeight;

            if(destRect.x1 < cb.x1)
            {
                sourceRect.x1 += cb.x1 - destRect.x1;
                destRect.x1 = cb.x1;
            }
            if(destRect.y1 < cb.y1)
            {
                sourceRect.y1 += cb.y1 - destRect.y1;
                destRect.y1 = cb.y1;
            }

            if(destRect.x2 > cb.x2) destRect.x2 = cb.x2;
            if(destRect.y2 > cb.y2) destRect.y2 = cb.y2;

            rc.x2 = destRect.x2 - destRect.x1;
            rc.y2 = destRect.y2 - destRect.y1;

            if(rc.x2 > sourceRect.x2 - sourceRect.x1) rc.x2 = sourceRect.x2 - sourceRect.x1;
            if(rc.y2 > sourceRect.y2 - sourceRect.y1) rc.y2 = sourceRect.y2 - sourceRect.y1;
            return rc;
        }

        public override void blend_color_vspan(int x, int y, int len, RGBA_Bytes[] colors, int colorsIndex, byte[] covers, int coversIndex, bool firstCoverForAll)
        {
            if (x > xmax()) return;
            if (x < xmin()) return;

            if (y < ymin())
            {
                int d = ymin() - y;
                len -= d;
                if (len <= 0) return;
                if (covers != null) coversIndex += d;
                colorsIndex += d;
                y = ymin();
            }
            if (y + len > ymax())
            {
                len = (ymax() - y + 1);
                if (len <= 0) return;
            }
            base.blend_color_vspan(x, y, len, colors, colorsIndex, covers, coversIndex, firstCoverForAll);
        }
    }

    public class ImageClippingProxyFloat : ImageProxyFloat
    {
        private rect_i m_ClippingRect;

        public const byte cover_full = 255;

        public ImageClippingProxyFloat(IImageFloat ren)
            : base(ren)
        {
            m_ClippingRect = new rect_i(0, 0, (int)ren.Width - 1, (int)ren.Height - 1);
        }

        public override void LinkToImage(IImageFloat ren)
        {
            base.LinkToImage(ren);
            m_ClippingRect = new rect_i(0, 0, (int)ren.Width - 1, (int)ren.Height - 1);
        }

        public bool SetClippingBox(int x1, int y1, int x2, int y2)
        {
            rect_i cb = new rect_i(x1, y1, x2, y2);
            cb.normalize();
            if (cb.clip(new rect_i(0, 0, (int)Width - 1, (int)Height - 1)))
            {
                m_ClippingRect = cb;
                return true;
            }
            m_ClippingRect.x1 = 1;
            m_ClippingRect.y1 = 1;
            m_ClippingRect.x2 = 0;
            m_ClippingRect.y2 = 0;
            return false;
        }

        public void reset_clipping(bool visibility)
        {
            if (visibility)
            {
                m_ClippingRect.x1 = 0;
                m_ClippingRect.y1 = 0;
                m_ClippingRect.x2 = (int)Width - 1;
                m_ClippingRect.y2 = (int)Height - 1;
            }
            else
            {
                m_ClippingRect.x1 = 1;
                m_ClippingRect.y1 = 1;
                m_ClippingRect.x2 = 0;
                m_ClippingRect.y2 = 0;
            }
        }

        public void clip_box_naked(int x1, int y1, int x2, int y2)
        {
            m_ClippingRect.x1 = x1;
            m_ClippingRect.y1 = y1;
            m_ClippingRect.x2 = x2;
            m_ClippingRect.y2 = y2;
        }

        public bool inbox(int x, int y)
        {
            return x >= m_ClippingRect.x1 && y >= m_ClippingRect.y1 &&
                   x <= m_ClippingRect.x2 && y <= m_ClippingRect.y2;
        }

        public rect_i clip_box() { return m_ClippingRect; }
        int xmin() { return m_ClippingRect.x1; }
        int ymin() { return m_ClippingRect.y1; }
        int xmax() { return m_ClippingRect.x2; }
        int ymax() { return m_ClippingRect.y2; }

        public rect_i bounding_clip_box() { return m_ClippingRect; }
        public int bounding_xmin() { return m_ClippingRect.x1; }
        public int bounding_ymin() { return m_ClippingRect.y1; }
        public int bounding_xmax() { return m_ClippingRect.x2; }
        public int bounding_ymax() { return m_ClippingRect.y2; }

        public void clear(IColorType in_c)
        {
            int y;
            RGBA_Floats colorFloat = in_c.GetAsRGBA_Floats();
            if (Width != 0)
            {
                for (y = 0; y < Height; y++)
                {
                    base.copy_hline(0, (int)y, (int)Width, colorFloat);
                }
            }
        }

        public override void copy_pixel(int x, int y, float[] c, int ByteOffset)
        {
            if (inbox(x, y))
            {
                base.copy_pixel(x, y, c, ByteOffset);
            }
        }

        public override RGBA_Floats pixel(int x, int y)
        {
            return inbox(x, y) ? base.pixel(x, y) : new RGBA_Floats();
        }

        public override void copy_hline(int x1, int y, int x2, RGBA_Floats c)
        {
            if (x1 > x2) { int t = (int)x2; x2 = (int)x1; x1 = t; }
            if (y > ymax()) return;
            if (y < ymin()) return;
            if (x1 > xmax()) return;
            if (x2 < xmin()) return;

            if (x1 < xmin()) x1 = xmin();
            if (x2 > xmax()) x2 = (int)xmax();

            base.copy_hline(x1, y, (int)(x2 - x1 + 1), c);
        }

        public override void copy_vline(int x, int y1, int y2, RGBA_Floats c)
        {
            if (y1 > y2) { int t = (int)y2; y2 = (int)y1; y1 = t; }
            if (x > xmax()) return;
            if (x < xmin()) return;
            if (y1 > ymax()) return;
            if (y2 < ymin()) return;

            if (y1 < ymin()) y1 = ymin();
            if (y2 > ymax()) y2 = (int)ymax();

            base.copy_vline(x, y1, (int)(y2 - y1 + 1), c);
        }

        public override void blend_hline(int x1, int y, int x2, RGBA_Floats c, byte cover)
        {
            if (x1 > x2)
            {
                int t = (int)x2;
                x2 = x1;
                x1 = t;
            }
            if (y > ymax())
                return;
            if (y < ymin())
                return;
            if (x1 > xmax())
                return;
            if (x2 < xmin())
                return;

            if (x1 < xmin())
                x1 = xmin();
            if (x2 > xmax())
                x2 = xmax();

            base.blend_hline(x1, y, x2, c, cover);
        }

        public override void blend_vline(int x, int y1, int y2, RGBA_Floats c, byte cover)
        {
            if (y1 > y2) { int t = y2; y2 = y1; y1 = t; }
            if (x > xmax()) return;
            if (x < xmin()) return;
            if (y1 > ymax()) return;
            if (y2 < ymin()) return;

            if (y1 < ymin()) y1 = ymin();
            if (y2 > ymax()) y2 = ymax();

            base.blend_vline(x, y1, y2, c, cover);
        }

        public override void blend_solid_hspan(int x, int y, int in_len, RGBA_Floats c, byte[] covers, int coversIndex)
        {
            int len = (int)in_len;
            if (y > ymax()) return;
            if (y < ymin()) return;

            if (x < xmin())
            {
                len -= xmin() - x;
                if (len <= 0) return;
                coversIndex += xmin() - x;
                x = xmin();
            }
            if (x + len > xmax())
            {
                len = xmax() - x + 1;
                if (len <= 0) return;
            }
            base.blend_solid_hspan(x, y, len, c, covers, coversIndex);
        }

        public override void blend_solid_vspan(int x, int y, int len, RGBA_Floats c, byte[] covers, int coversIndex)
        {
            if (x > xmax()) return;
            if (x < xmin()) return;

            if (y < ymin())
            {
                len -= (ymin() - y);
                if (len <= 0) return;
                coversIndex += ymin() - y;
                y = ymin();
            }
            if (y + len > ymax())
            {
                len = (ymax() - y + 1);
                if (len <= 0) return;
            }
            base.blend_solid_vspan(x, y, len, c, covers, coversIndex);
        }

        public override void copy_color_hspan(int x, int y, int len, RGBA_Floats[] colors, int colorsIndex)
        {
            if (y > ymax()) return;
            if (y < ymin()) return;

            if (x < xmin())
            {
                int d = xmin() - x;
                len -= d;
                if (len <= 0) return;
                colorsIndex += d;
                x = xmin();
            }
            if (x + len > xmax())
            {
                len = (xmax() - x + 1);
                if (len <= 0) return;
            }
            base.copy_color_hspan(x, y, len, colors, colorsIndex);
        }

        public override void copy_color_vspan(int x, int y, int len, RGBA_Floats[] colors, int colorsIndex)
        {
            if (x > xmax()) return;
            if (x < xmin()) return;

            if (y < ymin())
            {
                int d = ymin() - y;
                len -= d;
                if (len <= 0) return;
                colorsIndex += d;
                y = ymin();
            }
            if (y + len > ymax())
            {
                len = (ymax() - y + 1);
                if (len <= 0) return;
            }
            base.copy_color_vspan(x, y, len, colors, colorsIndex);
        }

        public override void blend_color_hspan(int x, int y, int in_len, RGBA_Floats[] colors, int colorsIndex, byte[] covers, int coversIndex, bool firstCoverForAll)
        {
            int len = (int)in_len;
            if (y > ymax())
                return;
            if (y < ymin())
                return;

            if (x < xmin())
            {
                int d = xmin() - x;
                len -= d;
                if (len <= 0) return;
                if (covers != null) coversIndex += d;
                colorsIndex += d;
                x = xmin();
            }
            if (x + len - 1 > xmax())
            {
                len = xmax() - x + 1;
                if (len <= 0) return;
            }

            base.blend_color_hspan(x, y, len, colors, colorsIndex, covers, coversIndex, firstCoverForAll);
        }

        public void copy_from(IImageFloat src)
        {
            CopyFrom(src, new rect_i(0, 0, (int)src.Width, (int)src.Height), 0, 0);
        }

        public override void SetPixelFromColor(float[] p, IColorType c)
        {
            throw new System.NotImplementedException();
        }

        public override void CopyFrom(IImageFloat sourceImage,
                       rect_i sourceImageRect,
                       int destXOffset,
                       int destYOffset)
        {
            rect_i destRect = sourceImageRect;
            destRect.Offset(destXOffset, destYOffset);

            rect_i clippedSourceRect = new rect_i();
            if (clippedSourceRect.intersect_rectangles(destRect, m_ClippingRect))
            {
                // move it back relative to the source
                clippedSourceRect.Offset(-destXOffset, -destYOffset);

                base.CopyFrom(sourceImage, clippedSourceRect, destXOffset, destYOffset);
            }
        }

        public rect_i clip_rect_area(ref rect_i destRect, ref rect_i sourceRect, int sourceWidth, int sourceHeight)
        {
            rect_i rc = new rect_i(0, 0, 0, 0);
            rect_i cb = clip_box();
            ++cb.x2;
            ++cb.y2;

            if (sourceRect.x1 < 0)
            {
                destRect.x1 -= sourceRect.x1;
                sourceRect.x1 = 0;
            }
            if (sourceRect.y1 < 0)
            {
                destRect.y1 -= sourceRect.y1;
                sourceRect.y1 = 0;
            }

            if (sourceRect.x2 > sourceWidth) sourceRect.x2 = sourceWidth;
            if (sourceRect.y2 > sourceHeight) sourceRect.y2 = sourceHeight;

            if (destRect.x1 < cb.x1)
            {
                sourceRect.x1 += cb.x1 - destRect.x1;
                destRect.x1 = cb.x1;
            }
            if (destRect.y1 < cb.y1)
            {
                sourceRect.y1 += cb.y1 - destRect.y1;
                destRect.y1 = cb.y1;
            }

            if (destRect.x2 > cb.x2) destRect.x2 = cb.x2;
            if (destRect.y2 > cb.y2) destRect.y2 = cb.y2;

            rc.x2 = destRect.x2 - destRect.x1;
            rc.y2 = destRect.y2 - destRect.y1;

            if (rc.x2 > sourceRect.x2 - sourceRect.x1) rc.x2 = sourceRect.x2 - sourceRect.x1;
            if (rc.y2 > sourceRect.y2 - sourceRect.y1) rc.y2 = sourceRect.y2 - sourceRect.y1;
            return rc;
        }

        public override void blend_color_vspan(int x, int y, int len, RGBA_Floats[] colors, int colorsIndex, byte[] covers, int coversIndex, bool firstCoverForAll)
        {
            if (x > xmax()) return;
            if (x < xmin()) return;

            if (y < ymin())
            {
                int d = ymin() - y;
                len -= d;
                if (len <= 0) return;
                if (covers != null) coversIndex += d;
                colorsIndex += d;
                y = ymin();
            }
            if (y + len > ymax())
            {
                len = (ymax() - y + 1);
                if (len <= 0) return;
            }
            base.blend_color_vspan(x, y, len, colors, colorsIndex, covers, coversIndex, firstCoverForAll);
        }
    }
}
